﻿#include "pch.h"
#include <mmsystem.h>
#include "Block1.h"
#include "Block2.h"
#include "Block3.h"
#include "Block4.h"
#include "Block5.h"
#include "Block6.h"
#include "Block7.h"
#include "Game.h"
#include "Resource.h"

ColorGrid Game::sExistentBlocks[Const::GAME_BOARD_GRID_COUNT_V][Const::GAME_BOARD_GRID_COUNT_H];

/**
 * 构造方法。
 * @param callback          回调
 * @param gameBoard         游戏区域
 * @param gameBoardDrawer   游戏区域绘图者
 * @param preview           预览区域
 * @param previewDrawer     预览区域绘图者
 */
Game::Game(CWnd* callback, CWnd* gameBoard, CDC* gameBoardDrawer, CWnd* preview, CDC* previewDrawer) :
    mCallback(callback),
    mCurBlock(NULL),
    mDifficulty(1),
    mEnableMusic(FALSE),
    mGameBoard(gameBoard),
    mGameBoardDrawer(gameBoardDrawer),
    mNextBlock(NULL),
    mPreview(preview), 
    mPreviewDrawer(previewDrawer),
    mScore(0),
    mStatus(END),
    mTop(Const::GAME_BOARD_GRID_COUNT_V + 1) {
    // 画游戏区域
    this->DrawGameBoard();

    // 画预览区域
    this->DrawPreview();

    // 置随机数种子
    srand((UINT)time(NULL));
}

/**
 * 析构方法。
 */
Game::~Game() {
    if (mCurBlock != NULL) {
        delete mCurBlock;
    }
    if (mNextBlock != NULL) {
        delete mNextBlock;
    }
}

/**
 * 检查是否存在满行。
 */
void Game::CheckRow() {
    // 更新"有方块的最高行"
    int top = mCurBlock->GetTop();
    if (top < mTop) {
        mTop = top;
    }

    int released = 0;   // 消除的行数
    int bottom = mCurBlock->GetBottom();

    // 从有方块的最上面一行，到当前方块所占的最下一行，逐行检查
    for (int i = mTop; i <= bottom; i++) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            // 如果该行有空的，就跳过该行
            if (!sExistentBlocks[i][j].mUsed) {
                break;
            }
            // 如果该行满了，消除该行
            if (j == Const::GAME_BOARD_GRID_COUNT_H - 1) {
                this->ReleaseRow(i);
                released++;
            }
        }
    }

    if (released > 0) {
        // 因为消行了，所以需要重绘
        this->DrawGameBoard();
        this->DrawExistentBlock();

        // 计算得分
        mScore += released * released * 100;

        // 发送通知
        mCallback->SendMessage(Const::UM_SCORE, mScore);

        // 计算难度
        if (mScore / Const::DIFF_BASE + 1 > mDifficulty && mDifficulty < Const::DIFF_MAX) {
            // 增加难度
            mDifficulty++;
            mCallback->KillTimer(Const::TIMER_ID);
            mCallback->SetTimer(Const::TIMER_ID, Const::TIMER_INIT - Const::TIMER_GROW * (mDifficulty - 1), NULL);

            // 发送通知
            mCallback->SendMessage(Const::UM_DIFFICULTY_UP, mDifficulty);
        }
    }
}

/**
 * 随机创建一种方块。
 * @return 方块
 */
Block* Game::CreateRandBlock() {
    int random = rand() % 7 + 1;

    switch (random) {
        case 1:
            return new Block1();
        case 2:
            return new Block2();
        case 3:
            return new Block3();
        case 4:
            return new Block4();
        case 5:
            return new Block5();
        case 6:
            return new Block6();
        case 7:
            return new Block7();
    }

    return new Block1();
}

/**
 * 画累积的方块。
 */
void Game::DrawExistentBlock() {
    for (int i = 0; i < Const::GAME_BOARD_GRID_COUNT_V; i++) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            if (sExistentBlocks[i][j].mUsed) {
                CRect rect(Const::GRID_SIZE * j, Const::GRID_SIZE * i, Const::GRID_SIZE * (j + 1), Const::GRID_SIZE * (i + 1));
                mGameBoardDrawer->FillSolidRect(rect, sExistentBlocks[i][j].mColor);
                mGameBoardDrawer->DrawEdge(rect, EDGE_RAISED, BF_RECT);
            }
        }
    }
}

/**
 * 画游戏区域。
 */
void Game::DrawGameBoard() {
    mGameBoardDrawer->FillSolidRect(0, 0, Const::GAME_BOARD_WIDTH, Const::GAME_BOARD_HEIGHT, Const::GAME_BOARD_COLOR);
    mGameBoard->Invalidate(FALSE);
}

/**
 * 画预览区域。
 */
void Game::DrawPreview() {
    mPreviewDrawer->FillSolidRect(0, 0, Const::PREVIEW_WIDTH, Const::PREVIEW_HEIGHT, Const::PREVIEW_COLOR);
    if (mNextBlock != NULL) {
        mNextBlock->EnterPreview(mPreviewDrawer);
    }
    mPreview->Invalidate(FALSE);
}

/**
 * 游戏结束。
 * @param isWon 胜利则为 TRUE，否则为 FALSE
 */
void Game::GameOver(BOOL isWon) {
    // 将状态设置为已结束
    mStatus = END;

    // 停止定时器
    mCallback->KillTimer(Const::TIMER_ID);

    // 停止播放音乐
    if (mEnableMusic) {
        PlaySound(NULL, NULL, NULL);
    }

    // 发送通知
    mCallback->SendMessage(Const::UM_GAME_OVER, isWon, 0);
}

/**
 * 获取游戏状态。
 * @param 游戏状态
 */
Game::Status Game::GetStatus() {
    return mStatus;
}

/**
 * 判断是否失败。
 * @return 失败则返回 TRUE，否则返回 FALSE
 */
BOOL Game::IsLost() {
    return mTop == 0 && sExistentBlocks[0][4].mUsed;
}

/**
 * 判断是否胜利。
 * @return 胜利则返回 TRUE，否则返回 FALSE
 */
BOOL Game::IsWon() {
    return mScore >= Const::WIN_NUMBER;
}

/**
 * 方块下落。
 */
void Game::MoveBlockDown() {
    if (mStatus != PLAYING) {
        return;
    }

    BOOL canMoveDown = mCurBlock->MoveDown();
    if (!canMoveDown) {
        // 检查是否存在满行
        this->CheckRow();

        // 判断是否胜利
        BOOL isWon = this->IsWon();
        if (isWon) {
            this->GameOver(TRUE);
            return;
        }

        // 判断是否失败
        BOOL isLost = this->IsLost();
        if (isLost) {
            this->GameOver(FALSE);
            return;
        }

        // 产生新的方块
        this->NextBlock();
    }
}

/**
 * 产生新的方块。
 */
void Game::NextBlock() {
    // 创建当前方块
    if (mCurBlock != NULL) {
        delete mCurBlock;
        mCurBlock = mNextBlock;
    } else {
        mCurBlock = this->CreateRandBlock();
    }

    // 创建下一个方块
    mNextBlock = this->CreateRandBlock();

    // 当前方块进入游戏区域
    mCurBlock->EnterGameboard(mGameBoard, mGameBoardDrawer);

    // 下一个方块进入预览区域
    this->DrawPreview();
}

/**
 * 按键事件的响应方法。
 * @param key   按键
 * @return 事件在此被处理则返回 TRUE，否则返回 FALSE
 */
BOOL Game::OnKeyPressed(UINT key) {
    if (mStatus != PLAYING) {
        return FALSE;
    }

    switch (key) {
        case VK_DOWN:
            this->MoveBlockDown();
            return TRUE;
        case VK_LEFT:
            mCurBlock->MoveLeft();
            return TRUE;
        case VK_RIGHT:
            mCurBlock->MoveRight();
            return TRUE;
        case VK_UP:
            mCurBlock->Transform();
            return TRUE;
    }

    return FALSE;
}

/**
 * 定时器事件的响应方法。
 */
void Game::OnTimer() {
    this->MoveBlockDown();
}

/**
 * 游戏暂停。
 */
void Game::Pause() {
    if (mStatus == PLAYING) {
        // 停止定时器
        mCallback->KillTimer(Const::TIMER_ID);

        // 停止播放音乐
        if (mEnableMusic) {
            PlaySound(NULL, NULL, NULL);
        }

        // 将状态设置为已暂停
        mStatus = PAUSED;
    } else if (mStatus == PAUSED) {
        // 开启定时器
        mCallback->SetTimer(Const::TIMER_ID, Const::TIMER_INIT - Const::TIMER_GROW * (mDifficulty - 1), NULL);

        // 播放音乐
        if (mEnableMusic) {
            PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
        }

        // 将状态设置为游戏中
        mStatus = PLAYING;
    }
}

/**
 * 播放音乐。
 * @return 正在播放音乐则为 TRUE，否则为 FALSE
 */
BOOL Game::PlayMusic() {
    mEnableMusic = !mEnableMusic;

    if (mEnableMusic) {
        if (mStatus == PLAYING) {
            PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
        }
    } else {
        PlaySound(NULL, NULL, NULL);
    }

    return mEnableMusic;
}

/**
 * 消行。
 * @param row   行号
 */
void Game::ReleaseRow(int row) {
    // 最上行
    if (row == 0) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            sExistentBlocks[row][j].mUsed = FALSE;
        }
        mTop++;
        return;
    }

    // 非最上行
    for (int i = row; i > mTop; i--) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            sExistentBlocks[i][j] = sExistentBlocks[i - 1][j];
        }
    }

    for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
        sExistentBlocks[mTop][j].mUsed = FALSE;
    }

    mTop++;
}

/**
 * 游戏开始。
 */
void Game::Start() {
    // 画游戏区域
    this->DrawGameBoard();

    // 画预览区域
    this->DrawPreview();

    // 创建方块
    ZeroMemory(sExistentBlocks, sizeof(sExistentBlocks));
    if (mCurBlock != NULL) {
        delete mCurBlock;
        mCurBlock = NULL;
    }
    if (mNextBlock != NULL) {
        delete mNextBlock;
        mNextBlock = NULL;
    }
    this->NextBlock();

    // 重置难度
    mDifficulty = 1;

    // 重置分数
    mScore = 0;

    // 开启定时器
    mCallback->KillTimer(Const::TIMER_ID);
    mCallback->SetTimer(Const::TIMER_ID, Const::TIMER_INIT, NULL);

    // 播放音乐
    if (mEnableMusic) {
        PlaySound(MAKEINTRESOURCE(IDR_MUSIC), NULL, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT | SND_LOOP);
    }

    // 将状态设置为游戏中
    mStatus = PLAYING;
}
